package app.Servidor;

import geral.pack.Carta;
import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Pedro Santos
 */
public class Servidor implements Runnable {

    private HashMap<String, Cliente> ListaClientes;
    private LinkedList UsersOn;
    private ServerUdp server;// Aguarda requisições Normais.
    private ServerUdp server2;// Aguarda confirmação de usuário Online.
    private boolean flagOn;// Flag de user Online.
    private Cliente user;
    private Configurações config;

    Servidor() {
        ListaClientes = new HashMap<String, Cliente>();
        UsersOn = new LinkedList();
        try {
            config = new Configurações(InetAddress.getLocalHost().getHostAddress(), 8080, 8081, 9090, 9095);
            //server = new ServerUdp(this, 8080);
        } catch (UnknownHostException ex) {
            Logger.getLogger(Servidor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public boolean CadastraCliente(String nick, String senha, InetAddress adr, String tipo, int port1, int port2) {
        if (!ListaClientes.containsKey(nick)) {
            Cliente c = new Cliente(nick, senha, adr, tipo, false, port1, port2);
            ListaClientes.put(nick, c);
            System.out.println("Cadastrou" + nick);
            return true;
        } else {
            System.out.println("Não Cadastrou!");
            return false;
        }
    }

    public boolean LogarCliente(String nick, String senha, InetAddress ip) {
        //Verificar se usuário existe ou já está on.
        String r = null;
        if (!ListaClientes.containsKey(nick) || this.getListaClientes().get(nick).getStatus()) {
            return false;
        } else {
            //Confirma Loguin
            Cliente h1 = ListaClientes.get(nick);
            if (h1.getSenha().equals(senha)) {
                this.getListaClientes().get(nick).setStatus(true);
                this.getListaClientes().get(nick).setIp(ip);
                System.out.println("Logou " + nick);
                return true;
            } else {
                return false;
            }
        }
    }
    /*
     * Cria uma lista com os jogadores Online e envia pra cliente do IP
     * especificado.
     */

    public void enviarLista(InetAddress ip) throws UnknownHostException {
        Collection<Cliente> values = this.ListaClientes.values();
        int num = this.ListaClientes.size();
        String UsersOn = String.valueOf(num);

        for (Cliente cliente : values) {
            //Monta a lista Com clientes On
            if (cliente.getStatus()) {
                UsersOn = UsersOn + "#" + cliente.getNick();
                System.out.println("ent");
            }
        }
        //System.out.println("ENVIARLISTA "+UsersOn);
        for (Cliente cliente : values) {
            //Envia para cada Cliente.
            ClientUdp c1 = new ClientUdp();
            //System.out.println(""+UsersOn);
            byte[] aux = UsersOn.getBytes();
            Carta msg = new Carta("", "", "lista on", 0, true, true, aux);
            aux = this.ObjToByte(msg);
            c1.send(ip, cliente.getPorta(), config.getPortSocket1(), aux);
        }
    }
    /*
     * Recebe uma Mensagem e Ecaminha ao destino. carta = mensagem em byte[].
     */

    public void encaminhar(String destino, byte[] carta) throws InterruptedException {
        Cliente cliente = this.ListaClientes.get(destino);
        ClientUdp c1 = new ClientUdp();
        InetAddress adr = cliente.getIp();
        int port = cliente.getPorta();
        c1.enviar(adr, port, carta);
    }

    public void ligar() {
        server = new ServerUdp(this, config.getPortServer1());//Porta 8080, mensagems.
        server2 = new ServerUdp(this, config.getPortServer2());//Confirmações de jogadores Online.
        ListaSender sender = new ListaSender(this);//Ficar enviando a lista de user Online.

        new Thread(server).start();
        new Thread(server2).start();
        new Thread(sender).start();
        new Thread(this).start();
        System.out.println("Servidor Iniciado");
    }

    public void desligar() {
        server.off();
    }

    public HashMap<String, Cliente> getListaClientes() {
        return ListaClientes;
    }

    public boolean isFlagOn() {
        return flagOn;
    }

    public void setFlagOn(boolean flagIsOn) {
        this.flagOn = flagIsOn;
    }

    public Cliente getUser() {
        return user;
    }

    public void setUser(Cliente user) {
        this.user = user;
    }

    @Override
    //Ficar verificando quais usuários estão Online.
    public void run() {
        //Ficar percorrendo o mapa de users.
        while (true) {
            Collection<Cliente> values = this.ListaClientes.values();

            for (Cliente cliente : values) {
                this.setFlagOn(false);
                ClientUdp c1 = new ClientUdp();
                Carta pack = new Carta("", "", "online", 0, true, true, ("online?").getBytes());
                byte[] aux = this.ObjToByte(pack);
                c1.send(cliente.getIp(), cliente.getPort2(), config.getPortSocket2(), aux);
                try {
                    Thread.sleep(80);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Servidor.class.getName()).log(Level.SEVERE, null, ex);
                }
                if (this.isFlagOn() && cliente.getNick().equals(user.getNick())) {
                    cliente.setStatus(true);
                } else {
                    cliente.setStatus(false);
                }
                try {
                    Thread.sleep(80);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Servidor.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

    }

    //Converte um Objeto serializavel para um Array de bytes.
    private byte[] ObjToByte(Object obj) {
        byte[] bytes = null;
        if (obj == null) {
            return null;
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            oos.close();
            bos.close();
            bytes = bos.toByteArray();
        } catch (IOException ex) {
            System.out.println("Erro de Serialização.");
        }
        return bytes;
    }

    //Converte um Objeto serializado para Object.
    private Object ByteToObj(byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
        } catch (IOException ex) {
            System.out.println("Erro IO");
        } catch (ClassNotFoundException ex) {
            System.out.println("CLass");
        }
        return obj;
    }
}
